#include <plugins_manager/qls_plugins_manager.h>

#include <QDir>
#include <QApplication>
#include <QSettings>
#include <QTextCodec>
#include <QJsonObject>
#include <QDebug>

#include <plugins_manager/qls_interface.h>
#include <themes_manager/qls_themes_manager.h>
#include <settings/qls_settings.h>

/* -------------------- 插件信息类 -------------------- */
/* 构造函数 */
PluginInfo::PluginInfo()
    : m_loader(nullptr), m_interface(nullptr), m_interfaceInfo(nullptr),
      m_isLoaded(false)
{
}

/* 运算符重载 == */
bool PluginInfo::operator==(const PluginInfo &other)
{
    return this->m_loader == other.m_loader;
}

/* 运算符重载 >> */
QList<PluginInfo> &PluginInfo::operator>>(QList<PluginInfo> &list)
{
    for (auto it = list.begin(); it != list.end(); it++) {
        /* 如果存在该插件，更新其他信息 */
        if (*it == *this) {
            if (it->m_interface != this->m_interface)
                it->m_interface = this->m_interface;
            if (it->m_interfaceInfo != this->m_interfaceInfo)
                it->m_interfaceInfo = this->m_interfaceInfo;
            if (it->m_absFilename != this->m_absFilename)
                it->m_absFilename = this->m_absFilename;
            if (it->m_filename != this->m_filename)
                it->m_filename = this->m_filename;
            if (it->m_isLoaded != this->m_isLoaded)
                it->m_isLoaded = this->m_isLoaded;
            return list;
        }
    }
    /* 不存在则直接插入列表 */
    list.push_back(*this);
    return list;
}

/* -------------------- 插件管理类 -------------------- */
QLsPluginsManager::QLsPluginsManager(
    QString pluginsDir /*  = "plugins/QCoreSystem" */)
    : m_themesManager(new QLsThemesManager), m_pluginsDir(pluginsDir),
      m_pluginsIniDir(
          QString("%1/%2%3.ini")
              .arg(QLsSettings::getInstallPath() + QLS_PATH_CONFIG, pluginsDir))
{
}

QLsPluginsManager::~QLsPluginsManager()
{
    UnloadAllPlugin();
}

/* 运算符重载 == */
bool QLsPluginsManager::operator==(const QLsPluginsManager &other)
{
    return this->m_pluginsDir == other.pluginsDir();
}

/* 插件路径 */
QString QLsPluginsManager::pluginsDir() const
{
    return m_pluginsDir;
}

void QLsPluginsManager::setPluginsDir(const QString &pluginsDir)
{
    m_pluginsDir = pluginsDir;
    m_pluginsIniDir =
        QString("%1/%2%3.ini")
            .arg(QLsSettings::getInstallPath(), QLS_PATH_CONFIG, pluginsDir);
}

/* 插件信息配置文件路径 */
QString QLsPluginsManager::pluginsIniDir() const
{
    return m_pluginsIniDir;
}

/* 更新本地插件：只获取信息，不直接加载 */
void QLsPluginsManager::RefreshLocalPlugins()
{
    /* 遍历插件库 */
    QDir pluginsDir(
        QString("%1/%2/%3")
            .arg(QLsSettings::getInstallPath(), "/lib/", m_pluginsDir));
    if (!pluginsDir.exists())
        return;

    for (QString &fileName : pluginsDir.entryList(QDir::Files)) {
        /* 加载插件 */
        QPluginLoader *pluginLoader =
            new QPluginLoader(pluginsDir.absoluteFilePath(fileName));

        /* 通过元数据匹配校验插件 */
        if (pluginLoader->metaData()["IID"].toString() !=
            QLsPluginService_iid) {
            if (pluginLoader) {
                delete pluginLoader;
                pluginLoader = nullptr;
            }
            continue;
        }

        /* 插入列表 */
        PluginInfo info;
        info.m_loader = pluginLoader;
        info.m_interfaceInfo =
            new InterfaceInfo(pluginLoader->metaData()["MetaData"].toObject());
        info.m_absFilename = pluginsDir.absoluteFilePath(fileName);
        info.m_filename = fileName;

        /* 只获取信息，不加载 */
        info.m_interface = nullptr;
        info.m_isLoaded = false;
        info >> m_pluginsList;
    }
}

/* 加载插件 */
bool QLsPluginsManager::LoadPlugin(PluginInfo &info)
{
    if (info.m_loader->isLoaded())
        return true;

    /* 加载插件 */
    if (!info.m_loader->load())
        return false;

    /* 获取插件唯一实例 */
    QObject *plugin = info.m_loader->instance();
    if (plugin) {
        /* 获取插件接口 */
        QLsPluginService *plugin_service =
            qobject_cast<QLsPluginService *>(plugin);
        if (plugin_service) {
            /* 更新插件信息 */
            info.m_interface = plugin_service;
            info.m_isLoaded = info.m_loader->isLoaded();
            info >> m_pluginsList;
            return true;
        }
    }

    return false;
}

bool QLsPluginsManager::LoadPlugin(const QString &filename)
{
    /* 当前是否已存在该插件，如果已存在，不需读取，直接加载 */
    for (PluginInfo info : m_pluginsList) {
        if (info.m_filename == filename) {
            return LoadPlugin(info);
        }
    }

    /* 遍历插件库 */
    QDir pluginsDir(
        QString("%1/%2/%3")
            .arg(QLsSettings::getInstallPath(), "/lib/", m_pluginsDir));
    if (!pluginsDir.exists()) {
        return false;
    }

    /* 加载插件 */
    QPluginLoader *pluginLoader =
        new QPluginLoader(pluginsDir.absoluteFilePath(filename));
    if (pluginLoader->metaData()["IID"].toString() != QLsPluginService_iid ||
        !pluginLoader->load()) {
        if (pluginLoader) {
            delete pluginLoader;
            pluginLoader = nullptr;
        }
        return false;
    }

    /* 获取插件唯一实例 */
    QObject *plugin = pluginLoader->instance();
    if (plugin) {
        /* 获取插件接口 */
        QLsPluginService *plugin_service =
            qobject_cast<QLsPluginService *>(plugin);
        if (plugin_service) {
            /* 插入列表 */
            PluginInfo info;
            info.m_loader = pluginLoader;
            info.m_interface = plugin_service;
            info.m_interfaceInfo = new InterfaceInfo(
                pluginLoader->metaData()["MetaData"].toObject());
            info.m_absFilename = pluginsDir.absoluteFilePath(filename);
            info.m_filename = filename;
            info.m_isLoaded = pluginLoader->isLoaded();
            info >> m_pluginsList;
            return true;
        }
    }

    return false;
}

QList<PluginInfo> QLsPluginsManager::GetPluginsList() const
{
    return m_pluginsList;
}

/* 卸载全部插件 */
void QLsPluginsManager::UnloadAllPlugin()
{
    foreach (PluginInfo info, m_pluginsList) {
        /* 只卸载插件，释放接口，不清除插件信息 */
        if (info.m_isLoaded && info.m_loader->unload()) {
            info.m_interface = nullptr;
            info.m_isLoaded = false;
            info >> m_pluginsList;
        }
    }
}

/* 卸载插件 */
bool QLsPluginsManager::UnloadPlugin(PluginInfo &info)
{
    /* 原来就是未加载状态，则直接返回卸载成功 */
    if (!info.m_isLoaded) {
        return true;
    }
    /* 卸载并成功 */
    if (info.m_isLoaded && info.m_loader->unload()) {
        info.m_interface = nullptr;
        info.m_isLoaded = false;
        info >> m_pluginsList;
        return true;
    }
    /* 卸载失败 */
    return false;
}
bool QLsPluginsManager::UnloadPluginByFilename(const QString &filename)
{
    /* 找到并卸载：相对路径 */
    for (PluginInfo &info : m_pluginsList) {
        if (info.m_filename == filename) {
            return UnloadPlugin(info);
        }
    }
    /* 没有该插件 */
    return false;
}
bool QLsPluginsManager::UnloadPluginByAbsFilename(const QString &absFilename)
{
    /* 找到并卸载：绝对路径 */
    for (PluginInfo &info : m_pluginsList) {
        if (info.m_absFilename == absFilename) {
            return UnloadPlugin(info);
        }
    }
    /* 没有该插件 */
    return false;
}

/* 根据插件名获取插件信息 */
PluginInfo QLsPluginsManager::GetPluginInfoByInterfaceName(
    const QString &interfaceName)
{
    for (PluginInfo &info : m_pluginsList) {
        if (info.m_interfaceInfo->m_name == interfaceName) {
            return info;
        }
    }
    return PluginInfo();
}

/* 检查必装插件是否加载 */
bool QLsPluginsManager::CheckMustLoadPlugins()
{
    /* 打开插件配置文件 */
    QSettings settings(m_pluginsIniDir, QSettings::IniFormat);
    settings.setIniCodec(QTextCodec::codecForName("UTF-8"));

    /* 遍历全部插件 */
    for (QString &pluginName : settings.childGroups()) {
        /* 找到必装插件 */
        settings.beginGroup(pluginName);
        if (settings.value("level", 0).toInt() != 1) {
            continue;
        }

        /* 判断是否存在 */
        bool flag = false;
        for (PluginInfo &info : m_pluginsList) {
            if (info.m_filename == pluginName) {
                flag = true;
                break;
            }
        }

        /* 如果没有，则将插件文件名存入列表 */
        if (!flag) {
            m_mustLoadButDisload << pluginName;
        }
    }

    return m_mustLoadButDisload.count() == 0;
}

QStringList QLsPluginsManager::mustLoadButDisload() const
{
    return m_mustLoadButDisload;
}

QLsThemesManager *QLsPluginsManager::themesManager() const
{
    return m_themesManager;
}
